﻿using System;
using Frogs.Stability.LeakyBucket;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Frogs.Stability.Tests
{
	[TestClass]
	public class LeakyBucketTests
	{
		[TestMethod]
		public void LeakyBucketCanOverflow()
		{
			ILeakyBucket leakyBucket = new DefaultLeakyBucket(10, 20, 2, 1000);
			leakyBucket.WaterLevel.Subscribe(state =>
				{
					switch (state)
					{
						case BucketState.Full:
							Assert.IsTrue(true);
							break;
						default:
							Assert.Fail();
							break;
					}
				});

			leakyBucket.FillUp();
		}

		[TestMethod]
		public void LeakyBucketStartsTimerOnDropsAdded()
		{
			MockBucketTimer mockTimer = new MockBucketTimer();
			ILeakyBucket leakyBucket = new DefaultLeakyBucket(10, 20, 2, mockTimer);

			Assert.IsFalse(mockTimer.IsRunning);
			leakyBucket.AddSingleDrop();
			Assert.IsTrue(mockTimer.IsRunning);
		}

		[TestMethod]
		public void LeakyBucketStopsTimerOnAllDropsRemoved()
		{
			MockBucketTimer mockTimer = new MockBucketTimer();
			ILeakyBucket leakyBucket = new DefaultLeakyBucket(10, 20, 2, mockTimer);

			Assert.IsFalse(mockTimer.IsRunning);
			leakyBucket.AddSingleDrop();
			Assert.IsTrue(mockTimer.IsRunning);

			mockTimer.SendElapsed();
			Assert.IsFalse(mockTimer.IsRunning);
		}

		[TestMethod]
		public void LeakyBucketWaterLevelEmptyToHalfFilled()
		{
			int halfFilled = 10;
			int filled = 20;
			MockBucketTimer mockTimer = new MockBucketTimer();
			ILeakyBucket leakyBucket = new DefaultLeakyBucket(halfFilled, filled, 2, mockTimer);
			leakyBucket.WaterLevel.Subscribe(state =>
			{
				switch (state)
				{
					case BucketState.HalfFilled:
						Assert.IsTrue(true);
						break;
					default:
						Assert.Fail();
						break;
				}
			});

			for (int iloop = 0; iloop < halfFilled; ++iloop)
			{
				leakyBucket.AddSingleDrop();
			}
		}

		[TestMethod]
		public void LeakyBucketWaterLevelEmptyToFullToHalfEmpty()
		{
			int halfFilled = 10;
			int filled = 20;
			MockBucketTimer mockTimer = new MockBucketTimer();
			ILeakyBucket leakyBucket = new DefaultLeakyBucket(halfFilled, filled, 1, mockTimer);
			var fullSubscription = leakyBucket.WaterLevel.Subscribe(state =>
				{
					switch (state)
					{
						case BucketState.Full:
							Assert.IsTrue(true);
							break;
						default:
							Assert.Fail();
							break;
					}
				});

			leakyBucket.FillUp();
			fullSubscription.Dispose();

			leakyBucket.WaterLevel.Subscribe(state =>
			{
				switch (state)
				{
					case BucketState.HalfDrained:
						Assert.IsTrue(true);
						break;
					default:
						Assert.Fail(string.Format("received state: {0}", state.ToString()));
						break;
				}
			});


			for (int iloop = 0; iloop < halfFilled; ++iloop)
			{
				mockTimer.SendElapsed();
			}
		}

	}
}
